home *** CD-ROM | disk | FTP | other *** search
/ Emulator Universe / Emulator Universe CD (1998).iso / CPC / Utils / CpcFile System / FS.C < prev    next >
Encoding:
C/C++ Source or Header  |  1996-02-08  |  43.9 KB  |  1,896 lines

  1.  
  2. /*                <<<<Last Modified: Thu Feb 08 15:08:10 1996>>>>
  3. ------------------------------------------------------------------------------
  4.  
  5.     =====
  6.     CPCfs  --  f s . c  --    Manageing the Filesystem
  7.     =====
  8.  
  9.     Version 0.85              (c) February '96 by Derik van Zuetphen
  10. ------------------------------------------------------------------------------
  11. */
  12.  
  13. #include <sys/stat.h>
  14.  
  15. #include "cpcfs.h"
  16.  
  17.  
  18. /****** Variables ******/
  19.  
  20. int    cur_trk = -1;        /* flag for no track loaded */
  21. int    cur_hd = 0;
  22. int    cur_blk = -1;
  23.  
  24.  
  25.  
  26. bool tag_ok () {
  27. /*   ^^^^^^ */
  28.     return    (strncmp("MV - CPC",(signed char*)disk_header.tag,8)==0);
  29. }
  30.  
  31.  
  32. void alloc_block(int blk,int file) {
  33. /*   ^^^^^^^^^^^
  34. <file> is only informational, real CP/M uses bitmaps */
  35.     if (file >= 0xFF) file = 0xFE;    /* FF marks a free block */
  36.     blk_alloc[blk]=file;
  37. }
  38.  
  39.  
  40. void free_block(int blk) {
  41. /*   ^^^^^^^^^^ */
  42.     blk_alloc[blk]=0xFF;
  43. }
  44.  
  45.  
  46. bool is_free_block(int blk) {
  47. /*   ^^^^^^^^^^^^^ */
  48.     return blk_alloc[blk]==0xFF;
  49. }
  50.  
  51.  
  52. void calc_allocation () {
  53. /*   ^^^^^^^^^^^^^^^ */
  54. int    i;
  55.     allocated_blks = 0;
  56.     free_blks = 0;
  57.     for (i=dpb->DBL; i<=dpb->DSM; i++)
  58.         if (is_free_block(i)) free_blks++;
  59.         else allocated_blks++;
  60.     percentage=100*allocated_blks/(float)(allocated_blks+free_blks);
  61.     total_blks = allocated_blks + free_blks;
  62. }
  63.  
  64.  
  65. bool inactive () {
  66. /*   ^^^^^^^^ */
  67.     if (*disk_header.tag==0) {
  68.         printm(1,"No image loaded!\n");
  69.         return TRUE;
  70.     } else
  71.         return FALSE;
  72. }
  73.  
  74.  
  75. /* Calculate track, sector, and head number of the first sector of block
  76. <blk>.
  77.  
  78. A quick picture:
  79.  
  80.         secs
  81.        .------^-------.
  82.        _______________
  83.       / -2- - - - - >/|  } heads
  84.      /______________/ | }
  85.       / | -1- - - - - > | |
  86.      :    |        | |
  87.      :    |        | |
  88.      :    |        | |
  89. trks =    |  -4- - - - - -| |
  90.      :    |        | |
  91.      :    | -3- - - - - > | |
  92.      :    |        | |
  93.       \ |_______________|/
  94.  
  95.  
  96. numbering scheme:
  97. first secs, then heads, then tracks
  98.     %s           /s%h      /s/h%t    has to be applied on running sec number
  99.  
  100. s = sec/trk, h = heads, t = tracks (here infinite)
  101.  
  102. */
  103.  
  104.  
  105. int trk_calc(int blk) {
  106. /*  ^^^^^^^^ */
  107.     return ((long)blk*dpb->BLS/dpb->BPS + dpb->OFS*dpb->SECS)
  108.         / (dpb->SECS*dpb->HDS);
  109. }
  110.  
  111. int sec_calc(int blk) {
  112. /*  ^^^^^^^^
  113. The returned number is not shifted by <sec_offset>! */
  114.     return ((long)blk*dpb->BLS/dpb->BPS + dpb->OFS*dpb->SECS) % dpb->SECS;
  115. }
  116.  
  117. int hd_calc(int blk) {
  118. /*  ^^^^^^^ */
  119.     return ((long)blk*dpb->BLS/dpb->BPS + dpb->OFS*dpb->SECS)
  120.         / dpb->SECS % dpb->HDS;
  121. }
  122.  
  123.  
  124. int blk_calc(int hd, int trk, int sec) {
  125. /*  ^^^^^^^^
  126. Return the blocknumber of position <hd>, <trk>, <sec> or -1, if it is a
  127. reserved position
  128. */
  129.     if (trk*dpb->HDS+hd < dpb->OFS) return -1;
  130.     return ((long)sec + hd*dpb->SECS
  131.         + trk*dpb->SECS*dpb->HDS
  132.         - dpb->OFS*dpb->SECS)
  133.         / (dpb->BLS/dpb->BPS);
  134. }
  135.  
  136.  
  137.  
  138. void abandonimage() {
  139. /*   ^^^^^^^^^^^^ */
  140.     *disk_header.tag = 0;
  141.     cur_trk = -1;
  142.     if (track)    {free(track); track=NULL;}
  143.     if (blk_alloc)    {free(blk_alloc); blk_alloc=NULL;}
  144.     if (directory)    {free(directory); directory=(DirEntry*)NULL;}
  145.     if (block_buffer){free(block_buffer); block_buffer=(uchar*)NULL;}       
  146.     errorf(FALSE,"Image \"%s\" abandoned!",imagename);
  147. }
  148.  
  149.  
  150.  
  151. /********
  152.   Tracks
  153.  ********/
  154.  
  155. int read_track (int hd, int trk) {
  156. /*  ^^^^^^^^^^ */
  157. long    n, pos;
  158.     if (trk == cur_trk && hd == cur_hd) {
  159.         return 0;
  160.     }
  161.  
  162.     printm(11,"[rt(%d,%d)] ",hd,trk);
  163.  
  164.     pos = (long)(trk*dpb->HDS + hd)    * (long)disk_header.tracksize;
  165.     n = lseek(imagefile,0x100L+pos,SEEK_SET);
  166.     if (n == -1L) {
  167.         errorf(TRUE,"Image currupt! I cannot position on track %d!",
  168.                                     trk);
  169.         abandonimage();
  170.         return -1;
  171.     }
  172.  
  173.     n = read(imagefile,track,disk_header.tracksize);
  174.     if (n != disk_header.tracksize) {
  175.         errorf(TRUE,"Image currupt! I can only read %ld bytes "
  176.                     "of track %d (instead of %d bytes)!",
  177.                     n,trk,disk_header.tracksize);
  178.         abandonimage();
  179.         return -1;
  180.     }
  181.     cur_trk = trk;
  182.     cur_hd    = hd;
  183.  
  184.     return 0;
  185. }
  186.  
  187.  
  188. int write_track() {
  189. /*  ^^^^^^^^^^^ */
  190. long    n,pos;
  191.     if (cur_trk == -1) return 0;
  192.  
  193.     printm(11,"[wt(%d,%d)] ",cur_hd,cur_trk);
  194.     pos = (long)(cur_trk*dpb->HDS + cur_hd)    * (long)disk_header.tracksize;
  195.     n = lseek(imagefile,0x100L+pos,SEEK_SET);
  196.     if (n == -1L) {
  197.         errorf(TRUE,"Image currupt! I cannot position on track %d!",
  198.                                 cur_trk);
  199.         abandonimage();
  200.         return -1;
  201.     }
  202.  
  203.     n = write(imagefile,track,disk_header.tracksize);
  204.     if (n != disk_header.tracksize) {
  205.         errorf(TRUE,"Something wrong! I cannot write %d bytes "
  206.                     "to track %d (only %d bytes written)!",
  207.                     disk_header.tracksize,cur_trk,n);
  208.         abandonimage();
  209.         return -1;
  210.     }
  211.     return 0;
  212. }
  213.  
  214.  
  215. bool next_sector (int *hd, int *trk, int *sec) {
  216. /*   ^^^^^^^^^^^
  217. Assumes <sec> without offset. Answer TRUE if the <trk> or <hd> is changed. */
  218.     (*sec)++;
  219.     if (*sec >= dpb->SECS) {
  220.         *sec -= dpb->SECS;
  221.         (*hd)++;
  222.         if (*hd >= dpb->HDS) {
  223.             *hd = 0;
  224.             (*trk)++;
  225.         }
  226.         return TRUE;
  227.     }
  228.     return FALSE;
  229. }
  230.  
  231.  
  232. uchar *read_block (int blk) {
  233. /*    ^^^^^^^^^^
  234. Read block <blk> into a buffer and return its address, or NULL on error */
  235. int    trk, sec, hd;
  236. int    filled = 0;
  237.  
  238.     if (blk==cur_blk) return block_buffer;
  239.  
  240.     printm(11,"[rb(%d)] ",blk);
  241.  
  242.     trk = trk_calc (blk);
  243.     sec = sec_calc (blk);
  244.     hd  = hd_calc (blk);
  245.  
  246.     while (filled < dpb->BLS) {
  247.         if (read_track(hd,trk)) return NULL;
  248.         memcpy(block_buffer+filled,track+0x100+sec*dpb->BPS,dpb->BPS);
  249.         filled += dpb->BPS;
  250.         next_sector (&hd,&trk,&sec);
  251.     }
  252.     cur_blk = blk;
  253.     return block_buffer;
  254. }
  255.  
  256.  
  257. uchar *write_block (int blk, char *buf) {
  258. /*    ^^^^^^^^^^^
  259. Return the written buffer or NULL on error */
  260. int    trk, sec, hd;
  261. int    filled = 0;
  262.  
  263.     printm(11,"[wb(%d)] ",blk);
  264.  
  265.     trk = trk_calc (blk);
  266.     sec = sec_calc (blk);
  267.     hd  = hd_calc (blk);
  268.  
  269.     while (filled < dpb->BLS) {
  270.         if (read_track(hd,trk)) return NULL;
  271.         memcpy(track+0x100+sec*dpb->BPS,buf+filled,dpb->BPS);
  272.         filled += dpb->BPS;
  273.         if (next_sector (&hd,&trk,&sec)) write_track();
  274.     }
  275.     write_track();
  276.     return buf;
  277. }
  278.  
  279.  
  280.  
  281. /***********
  282.   Directory
  283.  ***********/
  284.  
  285.  
  286. /* local definitions for glob_cpm_* */
  287.  
  288. #define GLOB_ENV_MAX    3
  289. int    glob_env = 0;
  290.     /* determines which global variable-set should <glob_cpm_*> use, */
  291.     /* necessary for nested globs */
  292.  
  293. uchar    pattern[GLOB_ENV_MAX][INPUTLEN];
  294. int    last_entry[GLOB_ENV_MAX];
  295.  
  296. int glob_cpm_next() {
  297. /*  ^^^^^^^^^^^^^
  298. Answer the next entry (or -1) with the same pattern as before */
  299. int    i;
  300. uchar    name[20];
  301.  
  302.     for (i=last_entry[glob_env]+1;i<=dpb->DRM;i++) {
  303.         if (!directory[i].first) continue;
  304.         build_cpm_name_32((signed char*)name, directory[i].user,
  305.                   (signed char*)directory[i].root,
  306.                   (signed char*)directory[i].ext);
  307.         if (match((signed char*)pattern[glob_env],(signed char*)name)) {
  308.             last_entry[glob_env] = i;
  309.             return i;
  310.         }
  311.     }
  312.     return -1;
  313. }
  314.  
  315.  
  316. int glob_cpm_file(char *pat) {
  317. /*  ^^^^^^^^^^^^^
  318. Scan the entries for the first file that matches <pattern> and answer its
  319. first (according to <first> flag, see DirEntry) entry number,
  320. If the pattern contains no filename part, *.* is assumed.
  321. Errorcode is -1, if pattern not found.
  322. The work is mostly deferred to <glob_cpm_next>. */
  323.  
  324. int    user;
  325. uchar    root[INPUTLEN], ext[INPUTLEN];
  326. const char errmsg[] = "Illegal filename \"%s\"";
  327.  
  328.     if (parse_cpm_filename(pat,&user,root,ext))
  329.         return errorf(FALSE,errmsg,pat);
  330.     upper(root);
  331.     upper(ext);
  332.     if (*root==0) {
  333.         if (user >= 0) {
  334.             strcpy(root,"*");
  335.             strcpy(ext,"*");
  336.         } else {
  337.         return errorf(FALSE,errmsg,pat);
  338.  
  339.         }
  340.     }
  341.     if (user==-1) user = cur_user;
  342.     build_cpm_name((signed char*)pattern[glob_env], user,
  343.         (signed char*)root, (signed char*)ext);
  344.     last_entry[glob_env] = -1;    /* thus start with 0 */
  345.     return glob_cpm_next();
  346. }
  347.  
  348.  
  349. /* local definitions for update_directory */
  350.  
  351. struct pair {uchar en; uchar ex;};
  352.  
  353. int cmp_pair(struct pair *x, struct pair *y) {
  354.     if (x->ex < y->ex) return -1;
  355.     if (x->ex > y->ex) return 1;
  356.     return 0;
  357. }
  358.  
  359. void update_directory() {
  360. /*   ^^^^^^^^^^^^^^^^
  361. (Re-)Organizes the directory structure (Packing the name, making a linked
  362. list) */
  363.  
  364. int    i, j;
  365.  
  366. /* a dynamic array of (entry,extent) pairs */
  367. struct pair    *map;
  368.  
  369.  
  370.     printm(10,"[ud] ");
  371.     map = (struct pair*)Malloc(sizeof(DirEntry)*(dpb->DRM+1));
  372.  
  373. /****** packing a name of kind "FOO    BAR" to "FOO.BAR\0" ******/
  374.     for (i=0;i<=dpb->DRM;i++) {
  375.         if (directory[i].user == 0xE5) continue;
  376.         build_cpm_name_32((signed char*)directory[i].name, -1,
  377.                   (signed char*)directory[i].root,
  378.                   (signed char*)directory[i].ext);
  379.     }
  380.  
  381.  
  382. /****** organizing the directory structure as linked list ******/
  383.  
  384. /* set entries in the directory to "not visited"
  385. <size> = 0 : never visit; <size> = -1 : not yet visited */
  386.     for (i=0;i<=dpb->DRM;i++) {
  387.         if (directory[i].user == 0xE5) /*NOT <filler>, E5 is required*/
  388.             directory[i].size = 0;    /* never visit empty entries */
  389.         else
  390.             directory[i].size = -1; /* not visited */
  391.         directory[i].first = FALSE;
  392.         directory[i].next  = -1;
  393.     };
  394.  
  395. /* scan the entries */
  396.     for (i=0;i<=dpb->DRM;i++) {
  397.         if (directory[i].size > -1) continue;
  398.  
  399. /* reset the map */
  400.         for (j=0;j<=dpb->DRM;j++) {map[j].en=j;map[j].ex=0xFF;}
  401.  
  402. /* fill the map with <extent> from the directory */
  403.         map[i].ex = directory[i].extent;
  404.         for (j=0;j<=dpb->DRM;j++) {
  405.             if ((directory[j].size == -1) &&
  406.                 (directory[j].user == directory[i].user) &&
  407.                 (i!=j) &&
  408.                 (strcmp((signed char*)directory[i].name,
  409.                     (signed char*)directory[j].name)==0)) {
  410.                     map[j].ex = directory[j].extent;
  411.                     directory[j].size = 0;
  412.             }
  413.         }
  414. /* sort the map according to map[].ex, not necessary in most cases */
  415.         qsort(map,dpb->DRM+1,sizeof(struct pair),
  416.             (int(*)(const void*,const void*))cmp_pair);
  417.  
  418. /* fill <first>, <size> and <next> from the map */
  419.         directory[map[0].en].first = TRUE;
  420.         j=1;
  421.         while (map[j].ex < 0xFF) {
  422.             directory[map[j-1].en].next = map[j].en;
  423.             j++;
  424.         }
  425.         directory[map[j-1].en].next = -1;
  426.  
  427. /* the filesize located in the first fileentry can be calculated from the
  428. <extent> and <rec> fields of the last fileentry */
  429.  
  430.         directory[map[0].en].size =
  431.             (long)directory[map[j-1].en].extent * EXTENTSIZE
  432.             + directory[map[j-1].en].rec * RECORDSIZE;
  433.  
  434.         
  435.     } /* for i */
  436.  
  437.     free(map); map=NULL;
  438. }
  439.  
  440.  
  441. void get_directory() {
  442. /*   ^^^^^^^^^^^^^ */
  443. int    i,j,off;
  444. uchar    *buf;
  445. int    mask;
  446.  
  447.     printm(10,"[rd] ");
  448. /* reading the directory data */
  449.     for (i=0;i<=dpb->DRM;i++) {
  450.         buf = read_block((signed)i*32/dpb->BLS);
  451.         off = i*32 % dpb->BLS;
  452. /* user, name, ... */
  453.         directory[i].user    = buf[off+0];
  454.         for (j=0;j<8;j++) directory[i].root[j] = buf[off+j+1] & 0x7F;
  455.         for (j=0;j<3;j++) directory[i].ext[j]  = buf[off+j+9] & 0x7F;   
  456.         directory[i].name[0]    = 0;
  457.             
  458.         directory[i].extent    = buf[off+12];
  459.         directory[i].unused[0]    = buf[off+13];
  460.         directory[i].unused[1]    = buf[off+14];
  461.         directory[i].rec    = buf[off+15];
  462.  
  463. /* attributes */
  464.         mask=0x1;
  465.         directory[i].attr = 0;
  466.         for (j=11;j>0;j--) {
  467.             if (buf[off+j]&0x80) directory[i].attr |= mask;
  468.             mask <<= 1;
  469.         }
  470.  
  471. /* block pointer */
  472.         for (j=0;j<16;j++) directory[i].blk[j] = 0;
  473.         if (BLKNR_SIZE==1) {
  474.             for (j=0;j<16;j++) {
  475.                 directory[i].blk[j] = buf[off+16+j];
  476.             }
  477.         } else if (BLKNR_SIZE==2) {
  478.             for (j=0;j<8;j++) {
  479.                 directory[i].blk[j] = buf[off+16+2*j]
  480.                             + 256 * buf[off+17+2*j];
  481.             }
  482.         }
  483.     }
  484.  
  485.     update_directory();
  486.     for (i=0;i<dpb->DBL;i++) alloc_block(i,0); /* 0 is not correct! */
  487.         
  488. /* marking the blocks as allocated */
  489.     for (j=0;j<=dpb->DSM;j++) free_block(j);
  490.     for (i=0;i<=dpb->DRM;i++) {
  491.         for (j=0;j<BLKNR;j++) {
  492.             if ((directory[i].user!=0xE5)&&(directory[i].blk[j]))
  493.                 alloc_block(directory[i].blk[j],i);
  494.         }
  495.     }
  496. }
  497.  
  498.  
  499. void put_directory() {
  500. /*   ^^^^^^^^^^^^^ */
  501. int    i, j, off;
  502. uchar    *buf;
  503. int    mask;
  504. int    block;
  505.  
  506.     printm(10,"[wd] ");
  507.     buf = block_buffer;    /* simply a shortcut */
  508.         
  509.     block = 0;
  510.     for (i=0;i<=dpb->DRM;i++) {
  511.         off=i*32 % dpb->BLS;
  512.  
  513.         buf[off] = directory[i].user;
  514.         for (j=0;j<8;j++) buf[off+j+1] = directory[i].root[j];
  515.         for (j=0;j<3;j++) buf[off+j+9] = directory[i].ext[j];   
  516.  
  517.         buf[off+12] = directory[i].extent;
  518.         buf[off+13] = directory[i].unused[0];
  519.         buf[off+14] = directory[i].unused[1];
  520.         buf[off+15] = directory[i].rec;
  521.  
  522.         mask=0x1;
  523.         for (j=11;j>0;j--) {
  524.             if (directory[i].attr & mask)
  525.                 buf[off+j] |= 0x80;
  526.             mask <<= 1;
  527.         };
  528.  
  529.         if (BLKNR_SIZE==1) {
  530.             for (j=0;j<16;j++) {
  531.                 buf[off+16+j] = directory[i].blk[j];
  532.             }
  533.         } else if (BLKNR_SIZE==2) {
  534.             for (j=0;j<8;j++) {
  535.                 buf[off+16+2*j] = directory[i].blk[j] % 256;
  536.                 buf[off+17+2*j] = directory[i].blk[j] / 256;
  537.             }
  538.         }
  539.  
  540. /* if next entry is in the next block, then write the current block */
  541.         if ((i+1)*32/(signed)dpb->BLS > block) {
  542.             write_block(block,buf);
  543.             block++;
  544.         }
  545.     }
  546. }
  547.  
  548.  
  549.  
  550. /*******
  551.   Image
  552.  *******/
  553.  
  554. void update_dpb(DPB_type *dpb, uchar *track) {
  555. /*   ^^^^^^^^^^
  556. Determine the extended DPB data out of <dpb> and the sample track <track>.
  557. Complete the extension parts of <dpb>. <track> must be read in first!
  558. */
  559.     dpb->BLS  = 1 << (dpb->BSH + 7); /* or 2^BSH*128 */
  560.  
  561. /* an image must exist, do not call form <format>! */
  562.     dpb->SEC1 = ((struct t_header*)track)->sector[0].sector;
  563.     dpb->SECS = ((struct t_header*)track)->SPT;
  564. /* the next two elements should already be set */
  565.     dpb->TRKS = disk_header.nbof_tracks;
  566.     dpb->HDS  = disk_header.nbof_heads;
  567.  
  568.     dpb->SYS  = (dpb->OFS>0) && (*(track+0x100)) != filler;
  569.     dpb->DBL  = 32 * (dpb->DRM+1) / dpb->BLS; /* or often CKS/8 */
  570.  
  571.     dpb->DSM = (dpb->TRKS*dpb->HDS*dpb->SECS) / (dpb->BLS/dpb->BPS);
  572. /* subtract reserved tracks */
  573.     dpb->DSM -= dpb->OFS * dpb->SECS / (dpb->BLS/dpb->BPS); 
  574.     dpb->DSM--;
  575.  
  576.     if (dpb->DSM>=255) {
  577. /* 2 byte pointer and 8 pointer per entry */
  578.         BLKNR_SIZE = 2;
  579.         BLKNR = 8;
  580.     } else {
  581. /* 1 byte pointer and 16 pointer per entry */
  582.         BLKNR_SIZE = 1;
  583.         BLKNR = 16;
  584.     }
  585. }       
  586.  
  587.  
  588. void close_image() {
  589. /*   ^^^^^^^^^^^ */
  590.     if (*disk_header.tag) {
  591.         printm(10,"[ci] ");
  592.         if (cur_trk > -1) write_track();
  593.         put_directory();
  594.         free(blk_alloc);    blk_alloc=NULL;
  595.         free(track);        track=NULL;
  596.         free(directory);    directory=(DirEntry*)NULL;
  597.         free(block_buffer);    block_buffer=(uchar*)NULL;            
  598.         *disk_header.tag = 0;
  599.         dpb = NULL;
  600.         close(imagefile);
  601.     }
  602.     cur_trk = -1;
  603.     cur_blk = -1;
  604. }
  605.  
  606.  
  607. int open_image(char *name) {
  608. /*  ^^^^^^^^^^
  609. alloc track buffer, blk_alloc, buffer, read directory */
  610.  
  611. int    n;
  612. char    dirsep[2] = {DIRSEPARATOR, 0};
  613. char    *p;
  614.  
  615.     if (*disk_header.tag) close_image();
  616. /* open file */
  617.     printm(10,"[oi] ");
  618.     imagefile = open(name,O_RDWR|O_BINARY,0);
  619.     imagename = name;    /* temporary, for <abandonimage> */
  620.     if (imagefile < 0) {
  621.         return errorf(TRUE,"Cannot open \"%s\"",name);
  622.     }
  623.     n = read(imagefile,&disk_header,0x100);
  624.     if (n!=0x100) {
  625.         errorf(FALSE,"Image corrupt! I cannot read image header "
  626.                             "(only %d bytes)!",n);
  627.         abandonimage();
  628.         return -1;
  629.     }
  630.     if (!tag_ok()) {
  631.         errorf(FALSE,"\"%s\" is not a DSK image!",name);
  632.         abandonimage();
  633.         return -1;
  634.     }
  635.     if ((disk_header.nbof_heads<1) || (disk_header.nbof_tracks<1)) {
  636.         errorf(FALSE,"--==>>> open_image: \"%s\"",name);
  637.         abandonimage();
  638.         return -1;
  639.     }
  640.             
  641. /* allocate memory */
  642.     track = Malloc(disk_header.tracksize);
  643.         
  644. /* set up varaibles */
  645.     filler = 0xE5;
  646.     cur_user=0;
  647.  
  648.     p = getwd(full_imagename);
  649.     if (p) strcpy(full_imagename,p);
  650.     if (full_imagename[strlen(full_imagename)-1]==DIRSEPARATOR)
  651.         full_imagename[strlen(full_imagename)-1]=0;
  652.     strcat(full_imagename,dirsep);
  653.     strcat(full_imagename,name);
  654. #if DOS
  655.     lower(full_imagename);
  656. #endif
  657.     if((imagename=strrchr(full_imagename,DIRSEPARATOR)))
  658.         imagename++;
  659.     else    imagename=full_imagename;
  660.         
  661. /* determine system/data-disk */
  662.     read_track(0,0);
  663.     cur_format = ((struct t_header*)track)->sector[0].sector; 
  664.     switch (cur_format) {
  665.     case SYSTEMFORMAT:
  666.         dpb=&DPB_store[SYSTEM_DPB]; update_dpb(dpb,track); break;
  667.     case IBMFORMAT: /* or Vortex format */
  668.         if (disk_header.nbof_heads==1) {
  669.             dpb=&DPB_store[IBM_DPB];
  670.             update_dpb(dpb,track);
  671.         } else {
  672.             dpb=&DPB_store[VORTEX_DPB];
  673.             update_dpb(dpb,track);
  674.             cur_format = VORTEXFORMAT;
  675.         }
  676.         break;
  677.     case DATAFORMAT:
  678.         dpb=&DPB_store[DATA_DPB]; update_dpb(dpb,track); break;
  679.     default:
  680.         errorf(FALSE,"Disk format not recognised!");
  681.         abandonimage();
  682.         return -1;
  683.         break;
  684.     }
  685.  
  686.  
  687. /* calculate number of blocks and allocate memory */
  688.     blk_alloc = Malloc(dpb->DSM+1);
  689.     directory = (DirEntry*)Malloc(sizeof(DirEntry)*(dpb->DRM+1));
  690.  
  691. /* allocate block buffer */
  692.     block_buffer = (uchar*)Malloc(dpb->BLS);
  693.         
  694. /* get directory information */
  695.     get_directory();
  696.     calc_allocation();
  697.  
  698.     return 0;
  699. }
  700.  
  701.  
  702. int comment_image(const char *text) {
  703. /*  ^^^^^^^^^^^^^
  704. Place <text> in the comment field of the image and save the image
  705. 48 bytes tag = 8 bytes required + 40 bytes free */
  706.  
  707. int     i;
  708.     memset(disk_header.tag+8,0,40);
  709.     i=0;
  710.     while (text[i] && i<40) {
  711.         *(disk_header.tag+8+i) = text[i];
  712.         i++;
  713.     }
  714.  
  715.     lseek(imagefile,0L,SEEK_SET);
  716.     if (write(imagefile,&disk_header,sizeof(disk_header)) < 0) {
  717.         return errorf(TRUE,"--==>>> comment_image");
  718.     }
  719.     return 0;
  720. }
  721.  
  722.  
  723.  
  724. /********
  725.   Blocks
  726.  ********/
  727.  
  728. int get_free_block() {
  729. /*  ^^^^^^^^^^^^^^ */
  730. static int next = 0;
  731. int    i;
  732.  
  733.     if (next > dpb->DSM) next = 0;
  734. /* try to allocate next block, if there was a previos one (next!=0) */
  735.     if ((next != 0) && (is_free_block(next))) return next++;
  736. /* try to find the first free block */
  737.     for (i=dpb->DBL;i<=dpb->DSM;i++) {
  738.         if (is_free_block(i)) return i;
  739.     }
  740.     return -1;
  741. }
  742.  
  743.  
  744. /*****************
  745.   FS Maintenance
  746.  *****************/
  747.  
  748.  
  749. struct {
  750.     int    flag;
  751.     uchar    type;
  752.     ushort    load;
  753.     ushort    jump;
  754.     ushort    size;
  755.     ushort    checksum;
  756.  
  757. } amsdos_header;
  758.  
  759.  
  760. void get_amshead(int ent) {
  761. /*   ^^^^^^^^^^^
  762. Read the first 128 bytes from the file and store them in the structure
  763. <amsdos_header>. Set the <flag> to 2, if it's a valid header; to 1, if it's
  764. invalid; and to 0 if the files is empty.
  765. */
  766.  
  767. int    i;
  768. ushort    sum = 0;
  769. uchar    *buf;
  770.  
  771.     if (directory[ent].blk[0] == 0) {
  772.         amsdos_header.flag = 0;
  773.         return;
  774.     }
  775.     buf = read_block((signed)directory[ent].blk[0]);
  776.     amsdos_header.type    = buf[18];
  777.     amsdos_header.load    = buf[21] + 256*buf[22];
  778.     amsdos_header.jump    = buf[26] + 256*buf[27];
  779.     amsdos_header.size    = buf[64] + 256*buf[65];
  780.     amsdos_header.checksum    = buf[67] + 256*buf[68];
  781.  
  782.     for (i=0;i<=66;i++) {
  783.         sum += buf[i];
  784.     }
  785.     if (sum==amsdos_header.checksum)
  786.         amsdos_header.flag = 2;
  787.     else
  788.         amsdos_header.flag = 1;
  789.  
  790.     return;
  791. }
  792.  
  793. /****** local function for dir() ******/
  794.  
  795. int cmp_array(int *x, int *y) {
  796. /*  ^^^^^^^^^
  797. Compares two filenames, whose entry numbers are <x> and <y> */
  798. int    res;
  799.  
  800.     if (directory[*x].user < directory[*y].user)        res = -1;
  801.     else if (directory[*x].user > directory[*y].user)    res = 1;
  802.     else {
  803.         res = strncmp((signed char*)directory[*x].root,
  804.                   (signed char*)directory[*y].root, 8);
  805.         if (res==0)
  806.             res = strncmp((signed char*)directory[*x].ext,
  807.                       (signed char*)directory[*y].ext, 3);
  808.     }
  809.     return res;
  810. }
  811.  
  812.  
  813. int dir(char *pat, int mask) {
  814. /*  ^^^
  815. <mask> is a "bit-or" of the following bits:
  816.     DIR_DOUBLE    default                     --00
  817.     DIR_WIDE    only names and sizes                --01
  818.     DIR_AMSHEAD    incl. AMSDOS Header                --10
  819.     DIR_LONG    incl. entries, records, all attributes        --11
  820.  
  821.     DIR_SORT    sorted                        -1--
  822. */
  823.  
  824.  
  825. long    total_bytes = 0;
  826. int    used_entries = 0;
  827. int    i,j;
  828. int    ent;
  829. int    files;
  830. int    mode;
  831. int    *array;         /* temporary dynamic array */
  832. char    upbuffer[INPUTLEN];    /* buffer for upper case conversion*/
  833. char    *buf;
  834. int    user;
  835. char    root[INPUTLEN];
  836. char    ext[INPUTLEN];
  837.  
  838.     array = (int*)Malloc(sizeof(int)*max(256,dpb->DRM+1));
  839.  
  840.     parse_cpm_filename(pat,&user,root,ext);
  841.     if (user==-1) user = cur_user;
  842.  
  843. /* calculate active users, entries */
  844.     files = 0;
  845.     for (i=0;i<256;i++) array[i]=0;
  846.     for (i=0;i<=dpb->DRM;i++) {
  847.         if (directory[i].user != 0xE5) used_entries++;
  848.         if (directory[i].first) {
  849.             array[directory[i].user]++;
  850.             files++;
  851.         }
  852.     }
  853.  
  854.     newpage("c");
  855.  
  856. /*** header ***/
  857.     strcpy(upbuffer,imagename);
  858.     upper(upbuffer);
  859.     printm(0,"Directory of Image: %s, User ", upbuffer);
  860.     if (user==-2)    printm(0,"ALL\n\n");
  861.     else        printm(0,"%-d\n\n",user);
  862.     nextline(); nextline();
  863.  
  864. /*** used users ***/
  865.     if (files>0) {
  866.         printm(0,"Used Users: ");
  867.         for (i=0;i<256;i++) {
  868.             if (array[i]!=0) {
  869.                 printm(0,"%d with %d file%s  \t",i,array[i],
  870.                             plural(array[i]));
  871.             }
  872.         }
  873.         printm(0,"\n\n");    nextline(); nextline();
  874.     }
  875. /*** files ***/
  876.  
  877. /* fetch all needed files */
  878.     for (i=0;i<=dpb->DRM;i++) array[i]=0xFFF; /* large but not negative */
  879.  
  880.     ent=glob_cpm_file(pat);
  881.     files=0;
  882.     while (ent>=0) {
  883.         array[files] = ent;
  884.         total_bytes += directory[ent].size;
  885.         ent=glob_cpm_next();
  886.         files++;
  887.  
  888.     }
  889.  
  890.     if (mask & DIR_SORT) qsort(array,files,sizeof(int),
  891.         (int(*)(const void*,const void*))cmp_array);
  892.  
  893. /* <array> contains now the entry numbers of the requested files in
  894. the requested order */
  895.  
  896.  
  897. /* output of the filenames */
  898.  
  899.     if (files==0) {
  900.         printm(0,"No files\n");
  901.         goto footer;
  902.     }
  903.  
  904.     i=0;
  905.     switch (mask&0x3) {
  906.     case DIR_DOUBLE:
  907.         printm(0," U Name            Size  Attr    Ent   "
  908.                "%c U Name            Size  Attr    Ent\n",vert);
  909.         nextline();
  910.         printm(0,"%s%c",repstr(hori,39),cross);
  911.         printm(0,"%s\n",repstr(hori,37));
  912.         nextline();
  913.         while (array[i]<0xFFF) {
  914.             j=array[i]; ent=1;
  915.             while(directory[j].next>-1) {
  916.                 ent++;
  917.                 j=directory[j].next;
  918.             }
  919.  
  920.             printm(0," %u %-12s  %6lu  %3s %c  %3d   ",
  921.                 directory[array[i]].user,
  922.                 directory[array[i]].name,
  923.                 directory[array[i]].size,
  924.                 show_attr(directory[array[i]].attr,ATTR_R,FALSE),
  925.                 ( directory[array[i]].attr&~ATTR_R? '+' : ' '),
  926.                 ent);
  927.             if (i%2==0)    printm(0," %c",vert);
  928.             else        {putcharm(0,10); nextline();}
  929.             i++;
  930.         };
  931.         break;
  932.     case DIR_WIDE:
  933.         printm(0,"Name          Size %c"
  934.              "Name          Size %c"
  935.              "Name          Size %c"             
  936.              "Name          Size\n",vert,vert,vert);
  937.         nextline();
  938.         printm(0,"%s%c",repstr(hori,19),cross);
  939.         printm(0,"%s%c",repstr(hori,19),cross);
  940.         printm(0,"%s%c",repstr(hori,19),cross);        
  941.         printm(0,"%s\n",repstr(hori,19));
  942.         nextline();
  943.         while (array[i]<0xFFF) {
  944.             printm(0,"%-12s%6lu ",
  945.                 directory[array[i]].name,
  946.                 directory[array[i]].size);
  947.             if (i%4!=3)    printm(0,"%c",vert);
  948.             else        {putcharm(0,10); nextline();}
  949.             i++;
  950.         };
  951.         break;
  952.     case DIR_AMSHEAD:
  953.         printm(0," U Name           Size Attr     Amsdos-Header\n");
  954.         nextline();
  955.         printm(0,"%s\n",repstr(hori,74));
  956.         nextline();
  957.         while (array[i]<0xFFF) {
  958.             printm(0,"%2u %-12s %6lu %3s %c    ",
  959.                 directory[array[i]].user,
  960.                 directory[array[i]].name,
  961.                 directory[array[i]].size,
  962.                 show_attr(directory[array[i]].attr,ATTR_R,FALSE),
  963.                 ( directory[array[i]].attr&~ATTR_R? '+' : ' '));
  964.  
  965.             get_amshead(array[i]);
  966.             if (amsdos_header.flag==0) {
  967.                 printm(0,"Empty");
  968.             } else if (amsdos_header.flag==1) {
  969.                 if (strncmp((signed char*)
  970.                   directory[array[i]].ext,"COM",3)==0)
  971.                     printm(0,"CP/M Program");
  972.                 else
  973.                     printm(0,"---");
  974.             } else {
  975.                 if (amsdos_header.type & 0x1)
  976.                     printm(0,"protected ");
  977.                 switch ((amsdos_header.type>>1) & 0x7) {
  978.                 case 0: printm(0,"BASIC "); break;
  979.                 case 1: printm(0,"Binary"); break;
  980.                 case 2: printm(0,"Screen"); break;
  981.                 case 3: printm(0,"ASCII "); break;
  982.                 default:printm(0,"Type=&%2X",
  983.                             amsdos_header.type);
  984.                 }
  985.  
  986.                 printm(0,"  Load=&%-4X, Jump=&%-4X, Size=&%-4X",
  987.                     amsdos_header.load,
  988.                     amsdos_header.jump,
  989.                     amsdos_header.size);
  990.             }
  991.  
  992.             putcharm(0,10);     nextline();
  993.  
  994.             i++;
  995.         };
  996.         break;
  997.     case DIR_LONG:
  998.         printm(0,"User  Name            Size  Attr    Ext. Attr. "
  999.              "Detect Entries Records Blocks\n");
  1000.         nextline();
  1001.         printm(0,"%s\n",repstr(hori,77));
  1002.         nextline();
  1003.         while (array[i]<0xFFF) {
  1004.             strcpy(upbuffer,
  1005.                 (signed char*)directory[array[i]].name);
  1006.             j=array[i]; ent=1;
  1007. /* detect mode */
  1008.             if (directory[array[i]].blk[0]==0)
  1009.                 mode = -1;
  1010.             else {
  1011. /* the only other function using <block_buffer> here is <get_amshead>,
  1012. but they do not interfere! */
  1013.                 buf = read_block(directory[array[i]].blk[0]);
  1014.                 mode = detectmode(buf,dpb->BLS);
  1015.             }
  1016. /* count entries */
  1017.             while(directory[j].next>-1) {
  1018.                 ent++;
  1019.                 j=directory[j].next;
  1020.             }
  1021.  
  1022.             printm(0,"%2u    %-12s  %6lu  %s %-6s%5u%8lu%7lu",
  1023.                 directory[array[i]].user,
  1024.                 upbuffer,
  1025.                 directory[array[i]].size,
  1026.                 show_all_attr(directory[array[i]].attr,TRUE),
  1027.                 mode==-1? "Empty"
  1028.                     : (mode==M_TEXT? "Text" : "Bin"),
  1029.                 ent,
  1030.                 (directory[array[i]].size+RECORDSIZE-1)/RECORDSIZE,
  1031.                 (directory[array[i]].size+(dpb->BLS-1))/dpb->BLS);
  1032.             putcharm(0,10);     nextline();
  1033.             i++;
  1034.         };
  1035.         break;
  1036.     } /* switch */
  1037.     printm(0,"\n");     nextline();
  1038.  
  1039.  
  1040. /*** footer ***/
  1041. footer:
  1042.  
  1043.     printm(0,"\n%d file%s in %lu Bytes\n",
  1044.         files, plural(files),
  1045.         total_bytes);
  1046.     nextline(); nextline();
  1047.     printm(0,"(%lu Bytes free, %lu Bytes allocated, %d entr%s of %d)\n",
  1048.         (long)free_blks*dpb->BLS,
  1049.         (long)allocated_blks*dpb->BLS,
  1050.         used_entries, plural_y(used_entries),
  1051.         dpb->DRM+1);
  1052.     nextline();
  1053.  
  1054.     free(array);
  1055.     return 0;
  1056. }
  1057.  
  1058.  
  1059. long delete(bool silent, char *pat) {
  1060. /*   ^^^^^^
  1061. Delete all files that match <pat>.
  1062. Answer the amount of deleted bytes (at least 0) */
  1063. long    freed = 0;
  1064. long    total_freed = 0;
  1065. int    ent, i;
  1066.  
  1067.  
  1068. /* warn, if <pat> contains *.* or is only usernumber */
  1069.     if (match("*\\*.\\**",pat) || match("*:",pat)) {
  1070.         if (!silent && Verb > 0) {
  1071.             printm(1,"Delete all in \"%s\"? ",pat);
  1072.             if (!confirmed()) return 0;
  1073.         }
  1074.     }
  1075.  
  1076.     ent = glob_cpm_file(pat);
  1077.     if (ent<0) {
  1078.         if (!silent) errorf(FALSE,"\"%s\" not found",pat);
  1079.         return 0;
  1080.     }
  1081.  
  1082.  
  1083.     while (ent>=0) {
  1084.         freed = 0;
  1085.         if (directory[ent].attr & ATTR_R) {
  1086.             if (!silent && Verb > 0) {
  1087.                 printm(1,"\"%u:%s\" readonly. Delete? ",
  1088.                     directory[ent].user,directory[ent].name);
  1089.                 if (!confirmed()) {
  1090.                     ent = glob_cpm_next();
  1091.                     continue;
  1092.                 }
  1093.             }
  1094.         }
  1095.  
  1096.         if (!silent) printm(3,"Deleting \"%u:%s\": ",
  1097.                 directory[ent].user,directory[ent].name);
  1098.         freed += directory[ent].size;
  1099.         while (ent>=0) {
  1100.             directory[ent].user = 0xE5;
  1101.             for (i=0;i<BLKNR;i++) {
  1102.                 if (directory[ent].blk[i]==0) break;
  1103.                 free_block(directory[ent].blk[i]);
  1104.             }
  1105.  
  1106.             ent = directory[ent].next;
  1107.         };
  1108.  
  1109.         if (!silent) printm(3,"%ld Bytes\n",freed);
  1110.         total_freed += freed;
  1111.         ent = glob_cpm_next();
  1112.     }
  1113.  
  1114.     update_directory();
  1115.     calc_allocation();
  1116.     return total_freed;
  1117. }
  1118.  
  1119.  
  1120. int change_attrib(char* pattern, int set, int reset) {
  1121. /*  ^^^^^^^^^^^^^
  1122. Change all attributes, masked by <mask> to <set> in all files matching
  1123. <pattern>. Answer -1 on error.
  1124. */
  1125. int    ent, ent0;
  1126.  
  1127.     ent = glob_cpm_file(pattern);
  1128.     if (ent<0)
  1129.         return errorf(FALSE,"\"%s\" not found",pattern);
  1130.     while (ent>=0) {
  1131.         printm(3,"Changing \"%s\" from \"%s\"",
  1132.             directory[ent].name,
  1133.             show_all_attr(directory[ent].attr,TRUE));
  1134.         ent0=ent;
  1135.         do {
  1136.             directory[ent].attr |= set;
  1137.             directory[ent].attr &= ~reset;
  1138.             ent = directory[ent].next;
  1139.         } while (ent>=0);
  1140.         printm(3," to \"%s\"\n",
  1141.             show_all_attr(directory[ent0].attr,TRUE));
  1142.         ent=glob_cpm_next();
  1143.     }
  1144.  
  1145.  
  1146.     return 0;
  1147. }
  1148.  
  1149.  
  1150. int sysgen(char *filename) {
  1151. /*  ^^^^^^
  1152. Copies the CP/M system from <filename> to the first two tracks of the image.
  1153. Does not test on Data-Format or sufficient space!
  1154. Answer -1 on error
  1155. */
  1156. char    buf[INPUTLEN];
  1157. int    cpm;
  1158. int    t;
  1159. short    tracksize = dpb->BPS*dpb->SECS; /* = Disk_header.tracksize - 0x100 */
  1160. long    n;
  1161.  
  1162.     strcpy(buf,filename);
  1163.     if (strchr(buf,'.')==NULL) strcat(buf,".cpm");
  1164.  
  1165.     cpm = open(buf,O_RDONLY|O_BINARY,0);
  1166.     if (cpm < 0) {
  1167.         return errorf(TRUE,"Cannot open \"%s\" for reading",buf);
  1168.     }
  1169.  
  1170.     for (t=0;t<dpb->OFS;t++) {
  1171. /* skip AMSDOS header and position on track <t> */
  1172.         n = lseek(cpm,(long)128+t*tracksize,SEEK_SET);
  1173.         if (n == -1L) {
  1174.             close(cpm);
  1175.             return errorf(TRUE,"CP/M Image currupt! "
  1176.                 "I cannot position on track %d",t);
  1177.         }
  1178.         read_track(t%dpb->HDS,t/dpb->HDS);
  1179.         n = read(cpm,track+0x100,tracksize);
  1180.         if (n != tracksize) {
  1181.             close(cpm);
  1182.             return errorf(TRUE,"CP/M Image currupt! "
  1183.                 "I can only read %d bytes (instead of %d bytes) ",
  1184.                 n, tracksize);
  1185.         }
  1186.         write_track();
  1187.     }
  1188.     printm(2,"CP/M \"%s\" copied to CPC filesystem\n",buf);
  1189.  
  1190.     close(cpm);
  1191.     dpb->SYS = TRUE;
  1192.  
  1193.     return 0;
  1194. }
  1195.  
  1196.  
  1197. int format(char* name, DPB_type *dpb) {
  1198. /*  ^^^^^^
  1199. Creates a new image with name <name> and format <dpb>. <dpb> must be out of
  1200. the <DPB_store>, not processed by <update_dpb>!
  1201. Answers -1 on error.
  1202. */
  1203. int    file;
  1204. int    i,h,j;
  1205. struct t_header *trhd;
  1206. time_t    now;
  1207.  
  1208.     file = creat(name,0644);
  1209.     if (file<0) {
  1210.         return errorf(TRUE,"Cannot open \"%s\" for writing",name);
  1211.     }
  1212.  
  1213.  
  1214. /* fill disk_header */
  1215.     printm(3,"Formatting (%s) ",show_format(dpb->ID));
  1216.     for (j=0;j<0x2F;j++) disk_header.tag[j] = 0;
  1217.     strcpy ((signed char*)disk_header.tag,"MV - CPCEMU / ");
  1218.     memset((disk_header.tag)+14,' ',20);
  1219.     now = time(NULL);
  1220.     strftime(((signed char*)disk_header.tag)+14,20,"%d %b %y %H:%M",
  1221.                             localtime(&now));
  1222.     disk_header.nbof_tracks    = dpb->TRKS;
  1223.     disk_header.nbof_heads    = dpb->HDS;
  1224.     disk_header.tracksize    = 0x100 + dpb->BPS*dpb->SECS;
  1225.     memset(disk_header.unused,0,0xCC);
  1226.     if (write(file,&disk_header,0x100) < 0) {
  1227.         return errorf(TRUE,"FORMAT");
  1228.     }
  1229.  
  1230.     track = Malloc(disk_header.tracksize);
  1231.     trhd = (struct t_header*)track;
  1232.     for (i=0;i<disk_header.nbof_tracks;i++)
  1233.         for (h=0;h<disk_header.nbof_heads;h++) {
  1234.         if (Break_Wish) {
  1235.             close(file);
  1236.             abandonimage();
  1237.             do_break();
  1238.         }
  1239. /* fill track_header */
  1240.         putcharm(3,'.'); fflush(stdout);
  1241.         strncpy((signed char*)trhd->tag,"Track-Info\r\n",0x10);
  1242.         trhd->track    = i;
  1243.         trhd->head    = h;
  1244.         trhd->unused[0] = 0;
  1245.         trhd->unused[1] = 0;
  1246.         trhd->BPS    = dpb->BPS/0x100;
  1247.         trhd->SPT    = dpb->SECS;
  1248.         trhd->GAP3    = 0x4E;
  1249.         trhd->filler    = 0xE5;
  1250.         for (j=0;j<dpb->SECS;j++) {
  1251.             trhd->sector[j].track    = i;
  1252.             trhd->sector[j].head    = h;
  1253.             trhd->sector[j].sector    = dpb->SEC1+j;
  1254.             trhd->sector[j].BPS    = dpb->BPS/0x100;
  1255.             trhd->sector[j].status1 = 0;
  1256.             trhd->sector[j].status2 = 0;
  1257.             trhd->sector[j].unused[0]=0;
  1258.             trhd->sector[j].unused[1]=0;
  1259.         }
  1260.         for (j=dpb->SECS; j<29; j++)  {
  1261.             memset(&(trhd->sector[j]),0,8);
  1262.         }
  1263.  
  1264.         memset(track+0x100,trhd->filler,disk_header.tracksize-0x100);
  1265.         if (write(file,track,disk_header.tracksize) < 0) {
  1266.             return errorf(TRUE,"FORMAT");
  1267.         }
  1268.     } /* end for i,h */
  1269.     printm(3,"   done\n");
  1270.     free(track);
  1271.     close(file);
  1272.     *disk_header.tag = 0;
  1273.     cur_trk = -1;
  1274.  
  1275.     return 0;
  1276. }
  1277.  
  1278.  
  1279. int ren_file(char *from, char *to) {
  1280. /*  ^^^^^^^^
  1281. Renames a file.
  1282. Wildcards are allowed and complete filenames must be given.
  1283. See ren_wild().
  1284. If a file is renamed to itself, do nothing.
  1285. The directory must be updated and written afterwards!
  1286. Answer -1 on error.
  1287. */
  1288. int    to_user;
  1289. char    to_root[INPUTLEN];
  1290. char    to_ext[INPUTLEN];
  1291. char    to_full[INPUTLEN];
  1292. int    from_user;
  1293. char    from_root[INPUTLEN];
  1294. char    from_ext[INPUTLEN];
  1295. char    from_full[INPUTLEN];
  1296. int    ent;
  1297. const char wild_fmt[] = "\"%s\" may not contain wildcards";
  1298.  
  1299.     upper(to);
  1300.     upper(from);
  1301.     if (has_wildcards('c',from)) {
  1302.         return errorf(FALSE,wild_fmt,from);
  1303.     }
  1304.     if (has_wildcards('c',to)) {
  1305.         return errorf(FALSE,wild_fmt,to);
  1306.     }
  1307.  
  1308.     parse_cpm_filename(from,&from_user,from_root,from_ext);
  1309.     if (from_user==-1) from_user = cur_user;
  1310.     if (from_user==-2) return errorf(FALSE,"--==>>> ren_file: wild user");
  1311.     if (*from_root==0)
  1312.         return errorf(FALSE,"No name in \"%s\"",from);
  1313.     build_cpm_name((signed char*)from_full, from_user,
  1314.         (signed char*)from_root, (signed char*)from_ext);
  1315.         
  1316.     parse_cpm_filename(to,&to_user,to_root,to_ext);
  1317.     if (to_user==-1) to_user = cur_user;
  1318.     if (*to_root==0) {
  1319.         strcpy(to_root,from_root);
  1320.         strcpy(to_ext,from_ext);
  1321.     }
  1322.     build_cpm_name((signed char*)to_full, to_user,
  1323.         (signed char*)to_root, (signed char*)to_ext);
  1324.  
  1325. /* test on identity of <to> and <from> */
  1326.     if (strcmp(to_full,from_full)==0) {
  1327.         printm(2,"Renaming \"%s\" to itself\n",from_full);
  1328.         return 0;
  1329.     }
  1330.     
  1331. /* check if already exists */
  1332.     if (glob_cpm_file(to_full)>=0) {
  1333.         if (Verb > 0) {
  1334.             printm(1,"\"%s\" already exists! Overwrite? ",to_full);
  1335.             if (confirmed())    delete(TRUE,to_full);
  1336.             else            return 0;
  1337.         } else return errorf(FALSE,"\"%s\" already exists",to_full);
  1338.     }
  1339.  
  1340.            ent = glob_cpm_file(from_full);
  1341.     if (ent<0) return errorf(FALSE,"\"%s\" not found",from_full);
  1342.  
  1343.     printm(2,"Renaming \"%u:%s\" to \"%s\"\n",
  1344.         directory[ent].user, directory[ent].name, to_full);
  1345.  
  1346.     do {
  1347.         directory[ent].user = to_user;
  1348.         str2mem((signed char*)directory[ent].root,
  1349.             (signed char*)to_root, 8);
  1350.         str2mem((signed char*)directory[ent].ext,
  1351.             (signed char*)to_ext, 3);
  1352.         ent = directory[ent].next;
  1353.     } while (ent>=0);
  1354.         
  1355.     return 0;
  1356. }
  1357.  
  1358.  
  1359. int ren_wild(char *pat, int us) {
  1360. /*  ^^^^^^^^
  1361. Renames all files that match <pat> to <user>.
  1362. The directory must be updated and written afterwards!
  1363. Answer -1 on error. */
  1364. int    ent;
  1365. char    src[20], trg[20];
  1366.  
  1367.     ent=glob_cpm_file(pat);
  1368.     if (ent<0) {
  1369.         errorf(FALSE,"\"%s\" not found",pat);
  1370.         return -1;
  1371.     }
  1372.     while (ent>=0) {
  1373.         sprintf(src,"%u:%s",directory[ent].user,directory[ent].name);
  1374.         sprintf(trg,"%u:%s",us,directory[ent].name);
  1375.         glob_env++;
  1376.         ren_file(src,trg);
  1377.         glob_env--;
  1378.         ent=glob_cpm_next();
  1379.     }
  1380.     return 0;
  1381. }
  1382.  
  1383.  
  1384. int copy_file(char *from, char *to) {
  1385. /*  ^^^^^^^^^
  1386. Copies a file to a new file with another name.
  1387. This preliminary version goes the way through put() and get() with a
  1388. temporary file.
  1389. Answer -1 on error. */
  1390.  
  1391. char    tempname[INPUTLEN];
  1392. int    err;
  1393.  
  1394.     tmp_nam(tempname);
  1395.     
  1396.     printm(3,"Copying \"%s\" to ",from);
  1397.     err=get(from,tempname);
  1398.     if (err==-1) {
  1399.         unlink(tempname);
  1400.         return -1;
  1401.     }
  1402.     err=put(tempname,to);
  1403.     if (err < 0) {
  1404.         unlink(tempname);
  1405.         return -1;
  1406.     }
  1407.     printm(3,"\"%s\"\n",to);
  1408.     
  1409.     unlink(tempname);
  1410.     return 0;
  1411. }
  1412.  
  1413.  
  1414. int copy_wild(char *pat, int us) {
  1415. /*  ^^^^^^^^^
  1416. Copies all files that match <pat> to <user>.
  1417. Answer -1 on error. */
  1418. int    ent;
  1419. char    src[20], trg[20];
  1420.  
  1421.     ent=glob_cpm_file(pat);
  1422.     if (ent<0) {
  1423.         errorf(FALSE,"\"%s\" not found",pat);
  1424.         return -1;
  1425.     }
  1426.     while (ent>=0) {
  1427.         sprintf(src,"%u:%s",directory[ent].user,directory[ent].name);
  1428.         sprintf(trg,"%u:%s",us,directory[ent].name);
  1429.         glob_env++;
  1430.         copy_file(src,trg);
  1431.         glob_env--;
  1432.         ent=glob_cpm_next();
  1433.     }
  1434.     return 0;
  1435. }
  1436.  
  1437.  
  1438.  
  1439. /*********
  1440.   Dumping
  1441.  *********/
  1442.  
  1443.  
  1444. int dumpdir (FILE* file) {
  1445. /*  ^^^^^^^ */
  1446. int    i,j;
  1447. char    n[INPUTLEN],e[INPUTLEN];
  1448.                     
  1449.     fprintf(file," #  U NAME         EX RE ATR BLOCKS\t\t\t\t\t    NEX\n");
  1450.     for (i=0;i<=dpb->DRM;i++) {
  1451.         strncpy(n,(signed char*)directory[i].root,8);     n[8] = 0;
  1452.         strncpy(e,(signed char*)directory[i].ext,3);    e[3] = 0;
  1453.         fprintf(file,"%2X%c%2X %s.%s %2X %2X ",
  1454.             i, (directory[i].first?'>':' '),
  1455.             directory[i].user,
  1456.             n, e,
  1457.             directory[i].extent, directory[i].rec);
  1458.         fprintf(file,"%2X%1X",(directory[i].attr&~0x7)>>3,
  1459.             directory[i].attr&0x7);
  1460.         for (j=0;j<BLKNR;j++) {
  1461.             if (directory[i].blk[j])
  1462.                 fprintf(file," %2X",directory[i].blk[j]);
  1463.             else
  1464.                 fprintf(file," --");
  1465.         }
  1466.  
  1467.         if (directory[i].next>=0)
  1468.             fprintf(file,">%2X",directory[i].next);
  1469.         else
  1470.             fprintf(file,"<<<");
  1471.         putc(10,file); 
  1472.         if (fflush(file)!=0)
  1473.             return errorf(TRUE,"DUMP -D");
  1474.     }
  1475.     return 0;
  1476. }
  1477.  
  1478.  
  1479. int dump(FILE *file, int block, int h, int t, int s) {
  1480. /*  ^^^^
  1481. Dump the contents of block <block> to <file> or if <block> = -1, the contents
  1482. of <h>,<t>,<s> to <file>.
  1483. */
  1484.  
  1485. int    hd, trk, sec;
  1486. int    i, j;
  1487. int    secs, k;
  1488. uchar    *p, *q;
  1489.  
  1490.     if (block == -1) {
  1491.         hd = h; trk = t; sec = s;
  1492.         secs=1;
  1493.         block = blk_calc(hd,trk,sec);
  1494.     } else {
  1495.         hd  = hd_calc(block);
  1496.         trk = trk_calc(block);
  1497.         sec = sec_calc(block);
  1498.         secs=dpb->BLS/dpb->BPS;
  1499.     }
  1500.  
  1501.  
  1502.     for (k=0;k<secs;k++) {
  1503.         read_track(hd,trk);
  1504.         
  1505.         fprintf(file,
  1506.             "\nBlock %d/Part %d   Head %d Track %d Sector %d\n\n",
  1507.             block,k,  hd,trk,sec);
  1508.         i = 0;
  1509.         p = track+0x100+sec*dpb->BPS;
  1510.         while (i<dpb->BPS) {
  1511.             fprintf(file,"%3X %c ",i,vert);
  1512.             q=p;
  1513.             for (j=0;j<16;j++) fprintf(file,"%2X ",*p++);
  1514.             fprintf(file," %c ",vert);
  1515.             p=q;
  1516.             for (j=0;j<16;j++) {
  1517.                 if (*p<32)
  1518.                     putc(' ',file);
  1519.                 else if (*p>=127)
  1520.                     putc('~',file);
  1521.                 else
  1522.                     putc(*p,file);
  1523.                 p++;
  1524.             }
  1525.             i+=16;
  1526.             putc(10,file);
  1527.             if (fflush(file)!=0)
  1528.                 return errorf(TRUE,"DUMP");
  1529.         }
  1530.  
  1531.         next_sector(&hd,&trk,&sec);
  1532.     };
  1533.     return 0;
  1534. }
  1535.  
  1536.  
  1537. int map(FILE *file) {
  1538. /* Writes the disk allocation map on <file>. */
  1539. int    h, t, s, b;
  1540. char    *str;
  1541.  
  1542.     str = repstr(' ',max(0,(dpb->SECS*3+2)-9));
  1543.                         /* 9 = len("%c Head %-2d") */
  1544.  
  1545.     fprintf(file,"      ");
  1546.     for (h=0;h<dpb->HDS;h++) {
  1547.         fprintf(file,"%c Head %-2d%s",vert,h,str);
  1548.     }
  1549.     fprintf(file,"\n");
  1550.  
  1551.     fprintf(file,"Track ");
  1552.     for (h=0;h<dpb->HDS;h++) {
  1553.         fprintf(file,"%c ",vert);
  1554.         for (s=0;s<dpb->SECS;s++) {
  1555.             fprintf(file,"%-2d ",s);
  1556.         }        
  1557.     }
  1558.     fprintf(file,"\n");
  1559.  
  1560.     str=repstr(hori,6); fprintf(file,"%s",str);
  1561.     for (h=0;h<dpb->HDS;h++) {
  1562.         str = repstr(hori,dpb->SECS*3+1);
  1563.         fprintf(file,"%c%s",cross,str);
  1564.     }
  1565.     fprintf(file,"\n");
  1566.  
  1567.     for (t=0;t<dpb->TRKS;t++) {
  1568.         fprintf(file,"%-4d  ",t);
  1569.         for (h=0;h<dpb->HDS;h++) {            
  1570.             fprintf(file,"%c ",vert);
  1571.             for (s=0;s<dpb->SECS;s++) {
  1572.  
  1573.                 b = blk_calc(h,t,s);
  1574.                 if (h+dpb->HDS*t < dpb->OFS) {
  1575.                     if (dpb->SYS) {
  1576.                         fprintf(file,"$$ ");
  1577.                     } else {
  1578.                         fprintf(file,"-- ");
  1579.                     }
  1580.                     continue;
  1581.                 }
  1582.                 if (b < dpb->DBL) {
  1583.                     fprintf(file,"DD ");
  1584.                     continue;
  1585.                 }
  1586.                 if (is_free_block(b)) {
  1587.                     fprintf(file,"-- ");
  1588.                 } else {    
  1589.                     fprintf(file,"%2X ",blk_alloc[b]);
  1590.                 }
  1591.             }
  1592.         }
  1593.         fprintf(file,"\n");
  1594.         if (fflush(file)!=0)
  1595.             return errorf(TRUE,"DUMP -M");
  1596.     }
  1597.     return 0;
  1598. }
  1599.  
  1600.  
  1601. /**********
  1602.   Transfer
  1603.  **********/
  1604.  
  1605.  
  1606. int detectmode (char *buf, int size)  {
  1607. /*  ^^^^^^^^^^
  1608. Count <size> characters in <buf>. If more than 70% are printable, the
  1609. file is considered as Text, otherwise Binary.
  1610. Returns M_TEXT or M_BIN.
  1611. */
  1612. long    printable, total;
  1613. int    k;
  1614.  
  1615.     printable = total = 0;
  1616.     for (k=0;k<=size; k++)    {
  1617.         if (buf[k] == CPM_EOF) break;
  1618.         if ((buf[k]==10)||(buf[k]==13)||
  1619.             ((buf[k]>=32)&&(buf[k]<=126)))  {
  1620.             printable++;
  1621.         }
  1622.         total++;
  1623.     }
  1624.     if (total==0) return M_BIN;    /* i.e. first char = ^Z */
  1625.     if ((100*printable/total) > 70)
  1626.         return M_TEXT;
  1627.     else
  1628.         return M_BIN;
  1629. }
  1630.  
  1631.  
  1632. long get(char *src, char *target) {
  1633. /*   ^^^
  1634. Get a file from CPC filesystem to local filesystem.
  1635. <src> is the CPC filename
  1636. <target> is the local filename
  1637. Returns number of bytes read or -1 if not found
  1638. */
  1639. int    ent, i, k;
  1640. int    file;
  1641. long    bytes = 0;    /* sum of bytes copied */
  1642. int    size;        /* size to copy at a chunk */
  1643. bool    last;        /* in last entry? */
  1644. int    localmode;
  1645. int    err;
  1646. uchar    *buf, *p;
  1647.  
  1648. /* open CP/M file */
  1649.     if (has_wildcards('c',src)) {
  1650.         return errorf(FALSE,"\"%s\" may not contain wildcards",src);
  1651.     }
  1652.     ent = glob_cpm_file(src);
  1653.     if (ent<0)    return errorf(FALSE,"\"%s\" not found",src);
  1654.  
  1655.  
  1656. /* open DOS file */
  1657.     if (access(target,F_OK)==0) {
  1658.         if (Verb > 0) {
  1659.             printm(1,"\"%s\" already exists! "
  1660.                     "Overwrite? ",target);
  1661.             if (!confirmed())  {
  1662.                 return -1;
  1663.             }
  1664.         }
  1665.     }
  1666.     file=creat(target,0644);
  1667.     if (file<0) return errorf(TRUE,"Cannot open \"%s\" for writing",target);
  1668.  
  1669.  
  1670.     localmode = -1; /* i.e. unknown */
  1671.     do {
  1672.         last = directory[ent].next == -1;    /* last entry? */
  1673.         for (i=0;i<BLKNR;i++) {
  1674.             if (directory[ent].blk[i]==0) {
  1675.                 if (!last) {
  1676.                     errorf(FALSE,"Directory entry for "
  1677.                       "\"%u:%s\" corrupt",
  1678.                       directory[ent].user,
  1679.                       directory[ent].name);
  1680.                     close(file);
  1681.                     return -1;
  1682.                 } else
  1683.                     break;
  1684.             }
  1685.  
  1686.             buf = read_block((signed)directory[ent].blk[i]);
  1687.             if (localmode == -1) {
  1688.                 if (mode!=M_AUTO)
  1689.                     localmode = mode;
  1690.                 else {
  1691.                     localmode = detectmode(buf,
  1692.                             (signed)dpb->BPS);
  1693. /*                    printm(3,"%s detected.\n",
  1694.                         show_mode(localmode));
  1695. */
  1696.                 }
  1697.             }
  1698.  
  1699. /* copy a whole block, except if on last blockpointer in last entry, then
  1700.    copy records */
  1701.             if (last &&
  1702.                 (directory[ent].blk[i]==BLKNR-1 ||
  1703.                  directory[ent].blk[i+1]==0)) {
  1704.                 size = directory[ent].rec*RECORDSIZE % dpb->BLS;
  1705.             } else {
  1706.                 size = dpb->BLS;
  1707.             }
  1708.             
  1709.             if (localmode==M_BIN) {
  1710.                 err=write(file,buf,size);
  1711.                 bytes += size;
  1712.             } else  {    /* up to ^Z */
  1713.                 p = memchr(buf,CPM_EOF,size);
  1714.                 if (p==NULL) {/* no ^Z */
  1715.                     err=write(file,buf,size);
  1716.                     bytes += size;
  1717.                 } else {/* get only a fraction of block */
  1718.                     k = p-buf;
  1719.                     err=write(file,buf,k);
  1720.                     bytes += k;
  1721.                 }
  1722.             }
  1723.             if (err<0) {
  1724.                 close(file);
  1725.                 return errorf(TRUE,"GET");
  1726.             }
  1727.         }
  1728.         ent = directory[ent].next;
  1729.     } while (ent>=0);
  1730.  
  1731.     close(file);
  1732.     return bytes;
  1733. }
  1734.  
  1735.  
  1736.  
  1737. long put(char *src, char *trg) {
  1738. /*   ^^^
  1739. Writes the DOS file <src> as CP/M File <trg>.
  1740. Returns number of bytes written or -1 if skipped, -2 if error */
  1741.  
  1742. uchar    *buf;
  1743. int    file;
  1744. int    entry,            /* current dir entry */
  1745.     blk,            /* pointer in data space */
  1746.     i;
  1747. long    size;
  1748. long    total;            /* total bytes */
  1749. long    entry_total;        /* total bytes for one entry */
  1750. int    used_entries;
  1751.  
  1752. int    usr;
  1753. char    rootname[INPUTLEN];
  1754. char    extension[INPUTLEN];
  1755. const char wild_fmt[] = "\"%s\" may not contain wildcards";
  1756. struct stat stat_buf;
  1757.  
  1758.     buf = block_buffer;    /* simply a shortcut */
  1759.  
  1760.     if (has_wildcards('d',src)) {
  1761.         errorf(FALSE,wild_fmt,src);
  1762.         return -2;
  1763.     }
  1764.     if (has_wildcards('c',trg)) {
  1765.         errorf(FALSE,wild_fmt,trg);
  1766.         return -2;
  1767.     }
  1768.     upper(trg);
  1769.  
  1770.  
  1771. /* test on existence and size of DOS file */
  1772.     if ((file = open(src, O_RDONLY|O_BINARY, 0)) == -1)  {
  1773.         errorf(TRUE,"Cannot read \"%s\"",src);
  1774.         return -2;
  1775.     }
  1776.     if (fstat(file,&stat_buf)) {
  1777.         errorf(TRUE,"--==>>> put: cannot stat \"%s\"",src);
  1778.         return -2;
  1779.     }
  1780.     if (stat_buf.st_size > (long)(dpb->DSM+1)*dpb->BLS) {
  1781.         errorf(FALSE,"\"%s\" is bigger than image",src);
  1782.         return -1;
  1783.     }
  1784.  
  1785. /* spilt the <trg> into name and extension */
  1786.     if (parse_cpm_filename(trg,&usr,rootname,extension)) return -1;
  1787.     if (usr==-1) usr = cur_user;
  1788.     if (*rootname==0) {
  1789.         errorf(FALSE,"No filename in \"%s\"",trg);
  1790.         return -2;
  1791.     }
  1792.  
  1793.  
  1794. /* test on existence in CP/M directory */
  1795.     if (glob_cpm_file(trg) >= 0) {
  1796.         if (Verb > 0) {
  1797.             printm(1,"\"%s\" already exists! Overwrite? ",trg);
  1798.             if (!confirmed())  {
  1799.                 close(file);
  1800.                 return -1;
  1801.             }
  1802.         }
  1803.         delete (TRUE,trg);
  1804.     }
  1805.  
  1806. /* walk thru the directory */
  1807.     total        = 0;
  1808.     used_entries    = 0;
  1809.     for (entry=0;entry<=dpb->DRM;entry++) {
  1810.         if (directory[entry].user != 0xE5) continue;
  1811.  
  1812. /* preread the first part, necessary if filesize multipe of 16k */
  1813.         size = read(file,buf,dpb->BLS);
  1814.         if (size==0) break;
  1815.  
  1816. /* fill name, user ... */
  1817.         directory[entry].user = usr;
  1818.         str2mem((signed char*)directory[entry].root,
  1819.             (signed char*)rootname, 8);
  1820.         str2mem((signed char*)directory[entry].ext,
  1821.             (signed char*)extension, 3);
  1822.         directory[entry].attr    = 0x00;
  1823.         directory[entry].unused[0]    = 0;    /* reserved for CP/M */
  1824.         directory[entry].unused[1]    = 0;
  1825.  
  1826. /* walk thru the block pointer area */
  1827.         entry_total = 0;
  1828.         for (i=0;i<BLKNR;i++) {
  1829.             if (i>0) size = read(file,buf,dpb->BLS);
  1830.             total += size;
  1831.             entry_total += size;
  1832.  
  1833.             if (size==0) break;
  1834. /* get a free block and copy the data */
  1835.             blk = get_free_block();
  1836.             if (blk==-1)  {
  1837.                 errorf(FALSE,"CPC Disk full!");
  1838.                 update_directory();
  1839.                 close(file);
  1840.                 delete(TRUE,trg);
  1841.                 return -2;
  1842.             }
  1843.             directory[entry].blk[i] = blk;
  1844. /* add a ^Z at the end */
  1845.             if (size<dpb->BLS) {
  1846.                 buf[size] = CPM_EOF;
  1847.                 size++;
  1848.             }
  1849. /* write the block */
  1850.             alloc_block(blk,entry);
  1851.             if (write_block(blk,(signed char*)buf)==NULL) {
  1852.                 errorf(FALSE,"Write error!");
  1853.                 close(file);
  1854.                 return -2;
  1855.             }
  1856.  
  1857.         }
  1858.  
  1859. /* finish up this direntry */
  1860.         while (i<BLKNR) directory[entry].blk[i++] = 0;
  1861.  
  1862. /* split the filesize such that <size_so_far> = <extent>*16k + <rec>*128byte */
  1863.         directory[entry].extent = entry_total/EXTENTSIZE
  1864.             + (dpb->EXM+1) * used_entries;
  1865.         directory[entry].rec = (entry_total+RECORDSIZE-1)
  1866.             / RECORDSIZE % RECORDSIZE;
  1867. /* if <rec>=0 and <extent> > 0, then set <rec>:=80h and decrement <extent> */
  1868.         if (directory[entry].rec==0 && directory[entry].extent>0) {
  1869.             directory[entry].rec = RECORDSIZE;
  1870.             directory[entry].extent--;
  1871.         }
  1872.  
  1873.         used_entries++;
  1874.         if (size==0) break;
  1875.     }
  1876.  
  1877.     close(file);
  1878.     update_directory();
  1879.  
  1880. /* uncopied data left */
  1881.     if (size>0) {
  1882.         errorf(FALSE,"CPC Directory full!");
  1883.         delete(TRUE,trg);
  1884.         return -2;
  1885.     }
  1886.  
  1887. /* <put_directory> needs <block_buffer> too, but it's available now after
  1888. <put> is nearly complete */
  1889.     put_directory();
  1890.     calc_allocation();
  1891.     return total;
  1892. }
  1893.  
  1894.  
  1895.  
  1896.